home *** CD-ROM | disk | FTP | other *** search
/ Cream of the Crop 25 / Cream of the Crop 25.iso / compress / tar321__.zip / SOURCES.ZIP / TAPE.C < prev    next >
C/C++ Source or Header  |  1997-03-30  |  17KB  |  681 lines

  1. /* tape.c - handle (tape) archive for the Tar program (see file tar.c)
  2.  * Author: T.V.Shaporev
  3.  * Creation date: 14 Dec 1990
  4.  * Contains both MS-DOS and UNIX specific codes
  5.  * Called by many functions - see files tar.c store.c extract.c
  6.  */
  7. #include <stdio.h>
  8. #include <errno.h>
  9.  
  10. #include "sysup.h"
  11. #include "modern.h"
  12. #include "zippipe.h"
  13. #include "lzwhead.h"
  14. #include "compress.h"
  15. #include "define.h"
  16.  
  17. #ifdef MODERN
  18. #    include <string.h>
  19. #    include <stdlib.h>
  20. #else
  21. #    ifdef M_XENIX
  22. #        include <string.h>
  23. #    else
  24.         int strlen();
  25.         char *strcpy(), *strncpy(), *mktemp();
  26. #    endif
  27. #endif
  28. #ifndef MSDOS
  29.     int  creat(), open(), read(), write(), close();
  30.     long lseek();
  31. #endif
  32.  
  33. static int n_read, mblock, nblock, rblock, wblock;
  34. static int indread, indwrite;
  35. static char rerror[] = "Tar: tape read error\n";
  36.  
  37. static int  sread  __ARGS__((char*, int));
  38. static int  swrite __ARGS__((char*, int));
  39. static int  sback  __ARGS__((int));
  40. #ifdef MSDOS
  41. extern int  dread  __ARGS__((char*, int));
  42. extern int  dwrite __ARGS__((char*, int));
  43. extern int  dback  __ARGS__((int));
  44.  
  45. extern int  qparse __ARGS__((char*));
  46. extern int  qbegin __ARGS__((void));
  47. extern int  qread  __ARGS__((char*, int));
  48. extern int  qwrite __ARGS__((char*, int));
  49. extern int  qback  __ARGS__((int));
  50.  
  51. extern int aspiparse __ARGS__((char*));
  52. extern int aspistart __ARGS__((void));
  53. extern int aspiread  __ARGS__((char*, int));
  54. extern int aspiwrite __ARGS__((char*, int));
  55. extern int aspiback  __ARGS__((int));
  56. #endif
  57. static void talign __ARGS__((long));
  58.  
  59. void printbs __ARGS__((int));
  60.  
  61. static void psyserr __ARGS__((void))
  62. {
  63.    extern char *sys_errlist[]; extern sys_nerr;
  64.  
  65.    if (errno < sys_nerr) (void)fprintf(myout, "%s\n", sys_errlist[errno]);
  66.    else                  (void)fprintf(myout, "error %d\n", errno);
  67. }
  68.  
  69. static int sread(buf, n) /* regular file reading */
  70. char *buf; register n;
  71. {
  72.    if ((n = read(handle, buf, n)) == -1) {
  73.       (void)fprintf(myout, "Tar: archive read error: "); psyserr();
  74.    }
  75.    return n;
  76. }
  77.  
  78. static int swrite(buf, n) /* regular file writing */
  79. char *buf; register n;
  80. {
  81.    if (write(hwrite, buf, n) != n) {
  82.       (void)fprintf(myout, "Tar: archive write error: ");
  83. #ifdef MSDOS
  84.       if (n != -1) (void)fprintf(myout,"disk full\n"); else
  85. #endif
  86.       psyserr();
  87.       n = -1;
  88.    }
  89.    return n;
  90. }
  91.  
  92. static int sback(n)
  93. register n;
  94. {
  95.    return lseek(handle, (long)-BLKSIZE*n, 1) < 0 ? -1 : n;
  96. }
  97.  
  98. #ifdef USE_COMPRESS
  99. static int zwrite __ARGS__((char*, int));
  100.  
  101. static int zwrite(buf, n) /* compressed file writing */
  102. char *buf; register n;
  103. {
  104.    cpiece(buf, n); return n;
  105. }
  106. #endif
  107.  
  108. static int gread  __ARGS__((char*, int));
  109. static int gwrite __ARGS__((char*, int));
  110.  
  111. static int gread(buf, n) /* deflated file reading */
  112. char *buf; register n;
  113. {
  114.    if ((n = unzread(buf, n)) == -1) {
  115.       (void)fprintf(myout, "Tar: unzip error: %s\n", ziperrlist[ziperror]);
  116.    }
  117.    return n;
  118. }
  119.  
  120. static int gwrite(buf, n) /* deflated file reading */
  121. char *buf; register n;
  122. {
  123.    if ((n = zipwrite(buf, n)) == -1) {
  124.       (void)fprintf(myout, "Tar: unzip error: %s\n", ziperrlist[ziperror]);
  125.    }
  126.    return n;
  127. }
  128.  
  129. static int ziperr __ARGS__((void))
  130. {
  131.    (void)fprintf(stderr, "Tar: zip error: %s\n", ziperrlist[ziperror]);
  132.    return ziperror == ZNOMEM ? ESMALL : ERINIT;
  133. }
  134.  
  135. #ifndef USE_COMPRESS
  136. static int twofault __ARGS__((char*, int));
  137.  
  138. /*ARGSUSED2*/ static int twofault(buf, n)
  139. char *buf; register n;
  140. {
  141. #ifdef __TURBOC__
  142.    (void)buf; (void)n;
  143. #endif
  144.    return -1;
  145. }
  146. #endif
  147.  
  148. /*ARGSUSED*/ static int onefault(n)
  149. int n;
  150. {
  151. #ifdef __TURBOC__
  152.    (void)n;
  153. #endif
  154.    return -1;
  155. }
  156.  
  157. static int (*pread) __ARGS__((char*, int)) = sread;
  158. static int (*pwrite)__ARGS__((char*, int)) = swrite;
  159. static int (*pback) __ARGS__((int))        = sback;
  160.  
  161. static int (*lread) __ARGS__((char*, int)) = sread;
  162. static int (*lwrite)__ARGS__((char*, int)) = swrite;
  163. static int (*lback) __ARGS__((int))        = sback;
  164. static int (*rcount)__ARGS__((char*, int)) = sread;
  165.  
  166. static int cntread  __ARGS__((char*, int));
  167.  
  168. static int cntread(buf, n)
  169. char *buf; register n;
  170. {
  171.    if ((n = (*rcount)(buf, n)) != -1) {
  172.       allblock += (BLKSIZE-1 + (unsigned)n) / BLKSIZE;
  173.    }
  174.    return n;
  175. }
  176.  
  177. static void talign(i)
  178. long i;
  179. {
  180.    if (i % BLKSIZE) {
  181.       (void)fprintf(myout, "Tar: tape blocksize error\n");
  182.       if (!i_flag) done(ERREAD);
  183.    }
  184. }
  185.  
  186. void printbs(bs)
  187. int bs;
  188. {
  189.    if (v_flag) (void)fprintf(myout, "Tar: blocksize = %d\n", bs);
  190. }
  191.  
  192. static void wrerror __ARGS__((void))
  193. {
  194.    (void)fprintf(myout, "Tar: tape write error\n");
  195.    done(EWRITE);
  196. }
  197.  
  198. static int indget;
  199. static int eof_already = FALSE;
  200. static int got_length;
  201. static int indput;
  202.  
  203. static int getbyte __ARGS__(( void ))
  204. {
  205.    if (eof_already) goto end;
  206.    if (indget >= got_length) {
  207.       if (pksize == 0) {
  208.          if ((got_length = (*lread)(pk_inp, MAXBLOCK*BLKSIZE)) < 0) goto err;
  209.          if ((sa.st_mode & S_IFMT) == S_IFCHR) talign((long)got_length);
  210.          pksize = got_length;
  211.          if (pksize && (pksize % BLKSIZE)==0) printbs(pksize/BLKSIZE);
  212.       } else {
  213.          if (got_length < pksize || got_length % BLKSIZE) goto end;
  214.          if ((got_length = (*lread)(pk_inp, pksize)) < 0) goto err;
  215.       }
  216.       if (got_length < 1) goto end;
  217.       indget = 0;
  218.    }
  219.    return ((unsigned char *)pk_inp)[indget++];
  220. err:
  221.    (void)fprintf(myout, rerror);
  222.    done(ERREAD);
  223. end:
  224.    eof_already = TRUE;
  225.    return EOF;
  226. }
  227.  
  228. static void pkflush __ARGS__((void))
  229. {
  230. #ifdef MSDOS
  231.    if (devtype != DEV_FILE && devtype != DEV_FLOP)
  232. #else
  233.    if (!isfile)
  234. #endif
  235.       while (indput < pksize) pk_out[indput++] = 0;
  236.  
  237.    if ((*lwrite)(pk_out, indput) < indput) wrerror();
  238.    indput = 0;
  239. }
  240.  
  241. static void putbyte(c)
  242. register c;
  243. {
  244.    if (indput >= pksize) {
  245. #ifndef pksize
  246.       /* Buffer size must not be less then 512 bytes, so we */
  247.       /* can wait for blocksize will be detected by reading */
  248.       if (indput < BLKSIZE) goto put;
  249.       if (pksize < BLKSIZE) pksize = BLKSIZE;
  250. #endif
  251.       pkflush();
  252.    }
  253. put:
  254.    pk_out[indput++] = c;
  255. }
  256.  
  257. int initape(name)
  258. char *name;
  259. {
  260. #ifdef UNIX
  261.    char tn[10]; register char *n;
  262. #endif
  263. #ifdef MSDOS
  264.    register k;
  265. #endif
  266.  
  267.    handle = -1;
  268.  
  269.    pread  = lread  = sread;
  270.    pwrite = lwrite = swrite;
  271.    pback  = lback  = sback;
  272.  
  273.    if (name && name[0]=='-' && name[1]==0) {
  274.       if ((a_flag && !c_flag) || d_flag) {
  275.          (void)fprintf(stderr, "Tar: can\'t update stdout\n");
  276.          return ERRARG;
  277.       }
  278. #ifdef myinp
  279.       if (j_flag
  280. #  ifndef MSDOS
  281.           || w_flag
  282. #  endif
  283.          ) {
  284.          (void)fprintf(stderr, "Tar: input must be free\n");
  285.          return ERRARG;
  286.       }
  287. #endif
  288.       handle = a_flag ? /* stdout */ 1 : /* stdin */ 0;
  289.       myout  = stderr;
  290.    } else {
  291. #ifdef UNIX
  292.       if (name && name[0]) {
  293.          n = name;
  294.       } else {
  295.          n = strcpy(tn, "/dev/mt0"); tn[7] = (ndrive & 7) | '0';
  296.       }
  297.       /* Do not create a file unless name specified */
  298.       handle = c_flag && n == name ? creat(n, 0666) :
  299.          open(n, a_flag && !d_flag ? OM_RDWR : OM_RDONLY);
  300.       if (handle < 0) {
  301.          cantopen(n); return ERINIT;
  302.       }
  303. #endif
  304.  
  305. #ifdef MSDOS
  306.       if (!name || !name[0]) {
  307.          devtype = DEV_FLOP;
  308.      inidisk();
  309.       } else if ((k=qparse(name)) != FALSE) {
  310.          if (k != TRUE) return ERRARG;
  311.          pread   = lread  = qread;
  312.          pwrite  = lwrite = qwrite;
  313.          pback   = lback  = qback;
  314.          devtype = DEV_QIC2;
  315.       } else if ((k=aspiparse(name)) != FALSE) {
  316.          if (k != TRUE) return ERRARG;
  317.          pread   = lread  = aspiread;
  318.          pwrite  = lwrite = aspiwrite;
  319.          pback   = lback  = aspiback;
  320.          devtype = DEV_ASPI;
  321.       } else {
  322.          if (!k_flag && defdev(name)==0) k_flag = TRUE;
  323.          if (k_flag) {
  324.             devtype = DEV_FLOP;
  325.         inidisk();
  326.          } else {
  327.             handle = lopen4(name,
  328.                !a_flag || d_flag ? OM_RDONLY :
  329.                   c_flag ? OM_WRONLY : OM_RDWR,
  330.                c_flag ? OA_CREATENEW+OA_TRUNCATE : OA_OPENONLY, 0);
  331.             if (handle < 0) {
  332.                cantopen(name); return ERINIT;
  333.             }
  334.             devtype = DEV_FILE;
  335.          }
  336.       }
  337.       if (devtype == DEV_FLOP) {
  338.          pread  = lread  = dread;
  339.          pwrite = lwrite = dwrite;
  340.          pback  = lback  = dback;
  341.       }
  342. #endif
  343.    }
  344.    if (t_flag) {/* yet another redirection */
  345.       rcount = lread; pread = lread = cntread; allblock = 0L;
  346.    }
  347.    if (handle < 0) {
  348.       sa.st_dev = sa.st_ino = -2;
  349.       sa.st_mode = S_IFCHR;
  350.    } else {
  351.       sa.st_mode = S_IFREG;
  352.    }
  353.    hwrite = handle;
  354.    n_read = 0;
  355.    return CORRECT;
  356. }
  357.  
  358. static void swapbufs __ARGS__((void))
  359. {
  360.    register j; register char *p;
  361.  
  362.    p = pk_inp; pk_inp = io_buf; io_buf = p;
  363.    p = pk_out; pk_out = io_2nd; io_2nd = p;
  364.  
  365.    j = pksize;
  366.    pksize = BLKSIZE*cblock;
  367.    cblock = j/BLKSIZE;
  368. }
  369.  
  370. int redirect()
  371. {
  372.    if (pktype == PKpLZW) {
  373.       /* Redirect IO */
  374.       pread  = dpiece;
  375. #ifdef USE_COMPRESS
  376.       pwrite = zwrite;
  377. #else
  378.       pwrite = twofault;
  379. #endif
  380.       pback  = onefault;
  381.  
  382.       swapbufs();
  383.  
  384.       if (x_flag || t_flag || d_flag) {
  385.          register j;
  386.  
  387.          indget = 0; got_length = (int)n_read;
  388.          if ((j=dbegin(getbyte))!=0) {
  389.             if (j < 0) {
  390.                outmem(stderr);
  391.                return ESMALL;
  392.             } else {
  393.                (void)fprintf(stderr,
  394.                   "Tar: archive is not in compressed format\n");
  395.                return ERINIT;
  396.             }
  397.          }
  398.       }
  399.       if (a_flag || d_flag) {
  400. #ifdef USE_COMPRESS
  401.          if (cbegin(lzwbits, putbyte, 0x7fffffffL) != lzwbits) {
  402.             outmem(stderr);
  403.             return ESMALL;
  404.          }
  405. #else
  406.          (void)fprintf(stderr,
  407.          "Tar: this restricted version does not support LZW compression\n");
  408.          return ERRARG;
  409. #endif
  410.       }
  411. #ifdef USE_COMPRESS
  412.       indput = 0;
  413. #endif
  414.    } else if (pktype == PKZIP) {
  415.       /* Redirect IO */
  416.       pread = gread; pwrite = gwrite; pback = onefault;
  417.       swapbufs();
  418.  
  419.       if (x_flag || t_flag || d_flag) {
  420.          indget = 0; got_length = (int)n_read;
  421.          if (unzopen(getbyte, ZIP_ANY) != 0) return ziperr();
  422.       }
  423.       indput = 0;
  424.       if (a_flag || d_flag) {
  425.          if (zipcreat(putbyte, (gnuzip ? ZIP_GNU : ZIP_PKW), ziplevel) != 0)
  426.             return ziperr();
  427.       }
  428.    }
  429.    rblock = mblock = cblock;
  430.    nblock = 0;
  431.    return CORRECT;
  432. }
  433.  
  434. int runtape()
  435. {
  436.    register k;
  437. #ifdef MSDOS
  438.    if      (devtype == DEV_QIC2) { if (qbegin())    return ERINIT; }
  439.    else if (devtype == DEV_ASPI) { if (aspistart()) return ERINIT; }
  440. #endif
  441.    if (!cblock && c_flag) printbs(cblock = MAXBLOCK);
  442.    if ((k = redirect()) != CORRECT) return k;
  443.    if (c_flag && !mblock) printbs(cblock = mblock = MAXBLOCK);
  444.    wblock = 0;
  445.    return CORRECT;
  446. }
  447.  
  448. void duptape(n)
  449. char *n;
  450. {
  451.    register i;
  452.    static char template[] = "XXXXXX";
  453.    extern char *mktemp __ARGS__((char*));
  454.  
  455.    io_2nd = getbuf(BLKSIZE * (mblock ? mblock : MAXBLOCK));
  456.    if (!io_2nd) done(ESMALL);
  457.  
  458.    i=strlen(n);
  459.    while (i>0 &&
  460. #ifdef MSDOS
  461.                  n[i-1]!='\\' && n[i-1]!=':' &&
  462. #endif
  463.                                                 n[i-1]!='/') --i;
  464.    scratch = salloc((int)(i + sizeof(template)));
  465.    (void)strncpy(scratch, n, i);
  466.    (void)strcpy (scratch+i, template);
  467.    (void)mktemp (scratch);
  468. #ifdef UNIX
  469.    hwrite = creat(scratch, 0666);
  470. #endif
  471. #ifdef MSDOS
  472.    hwrite = creat(scratch, 0);
  473. #endif
  474.    if (hwrite < 0) {
  475.       (void)fprintf(myout, "Tar: can\'t create scratch file\n");
  476.       done(EWRITE);
  477.    }
  478. }
  479.  
  480. void backtape()
  481. {
  482.    register i;
  483.  
  484.    talign((long)n_read);
  485.    if ((i = (int)(n_read / BLKSIZE % mblock)) == 0) i = mblock;
  486.    if (!isfile) {
  487.       register j;
  488.  
  489.       if ((*pback)(i)!=i) goto fault;
  490.       if ((j=(*pread)(io_buf, BLKSIZE*rblock))<BLKSIZE) goto fault;
  491.       n_read += j;
  492.    }
  493.    if ((*pback)(i) != i) goto fault;
  494.    wblock = --rblock; indread = indwrite = 0;
  495.    return;
  496. fault:
  497.    (void)fprintf(myout, "Tar: tape seek fault\n");
  498.    done(ERREAD);
  499. }
  500.  
  501. void endtape()
  502. {
  503.    nullblock(steptape());
  504.    nullblock(steptape()); /* old stupid programs require 2nd block */
  505.    if (wblock) {
  506.       if ((*pwrite)(io_2nd, BLKSIZE*((
  507. #ifdef MSDOS
  508.                      devtype == DEV_FILE || devtype == DEV_FLOP
  509. #else
  510.                      isfile
  511. #endif
  512. #ifdef USE_COMPRESS
  513.                      || pwrite == zwrite
  514. #endif
  515.                      || pwrite == gwrite)
  516.                      && wblock < mblock ? wblock : mblock)) < 0) {
  517.          wrerror();
  518.       }
  519.    }
  520. #ifdef USE_COMPRESS
  521.    if (pwrite == zwrite) {/* compression on output */
  522.       if (cflush() < 0) wrerror();
  523.       if (indput > 0) pkflush();
  524.    } else
  525. #endif
  526.    if (pwrite == gwrite) {/* zip compression */
  527.       if (zipclose() == -1L) wrerror();
  528.       if (indput > 0) pkflush();
  529.    }
  530.    if (hwrite >= 0 && close(hwrite)!=0) {
  531.       (void)fprintf(myout, "Tar: tape close error\n");
  532.       done(EWRITE);
  533.    }
  534. }
  535.  
  536. static int readbuf __ARGS__(( void ))
  537. {
  538.    register i;
  539.  
  540.    for (;;) {
  541.       if (mblock) {
  542.          if ((i=(*pread)(io_buf, BLKSIZE*mblock)) < BLKSIZE || i%BLKSIZE!=0) {
  543.             (void)fprintf(myout, rerror);
  544.             if (i_flag) return -1;
  545.             done(ERREAD);
  546.          }
  547.          n_read += i;
  548.          nblock = i / BLKSIZE;
  549.       } else {
  550.          if ((i = (*pread)(io_buf, MAXBLOCK*BLKSIZE)) < 1) {
  551.             (void)fprintf(myout, rerror);
  552.             if (i_flag) return -1;
  553.             done(ERREAD);
  554.          }
  555.          n_read += i;
  556.          if (i % BLKSIZE != 0 && pktest((unsigned char*)io_buf) == CORRECT) {
  557.             mblock = cblock; n_read = 0; continue;
  558.          }
  559.          talign((long)i);
  560.          mblock = nblock = i / BLKSIZE;
  561.          printbs(cblock = mblock);
  562.       }
  563.       break;
  564.    }
  565.    rblock = 0;
  566.    return 0;
  567. }
  568.  
  569. header *readtape()
  570. {
  571.    if (rblock>=nblock && readbuf()!=0) return NULL;
  572.    indread = 0;
  573.    return (header *)(io_buf + BLKSIZE * rblock++);
  574. }
  575.  
  576. int readbyte()
  577. {
  578.    register c;
  579.  
  580.    if (indread == 0) ++rblock; /* get 1-st byte - take all the block */
  581.    if (rblock>nblock) {
  582.       if (readbuf()!=0) return -1;
  583.       rblock = 1; /* preserve 1-st block from readtape() */
  584.    }
  585.    c = ((unsigned char *)(io_buf + BLKSIZE * (rblock-1)))[indread];
  586.    indread = (indread+1) & (BLKSIZE-1);
  587.    return c;
  588. }
  589.  
  590. void bacouple()
  591. /* return to the beginning of block after try to uncompress */
  592. {
  593.    indread = 0; --rblock;
  594. }
  595.  
  596. int readarch(h, length)
  597. int h; long length;
  598. {
  599.    register i;
  600.  
  601.    while (length > 0) {
  602.       if (rblock>=nblock && readbuf()!=0) return -1;
  603.       if ((i = (nblock-rblock)*BLKSIZE) > length) i = (int)length;
  604.       if (write(h, io_buf + rblock * BLKSIZE, i)!=i)
  605.          extwrerr();
  606.       rblock += (i + BLKSIZE-1) / BLKSIZE;
  607.       length -= i;
  608.    }
  609.    indread = 0;
  610.    return 0;
  611. }
  612.  
  613. header *steptape()
  614. {
  615.    if (!mblock) mblock = 1;
  616.    if (wblock >= mblock) {
  617.       if ((*pwrite)(io_2nd, BLKSIZE*mblock) < 0) wrerror();
  618.       wblock = 0;
  619.    }
  620.    indwrite = 0;
  621.    return (header *)(io_2nd + BLKSIZE * wblock++);
  622. }
  623.  
  624. void writebyte(c)
  625. int c;
  626. {
  627.    if (indwrite == 0) ++wblock;
  628.    if (wblock > mblock) {
  629.       if ((*pwrite)(io_2nd, BLKSIZE*mblock) < 0) wrerror();
  630.       wblock = 1;
  631.    }
  632.    ((unsigned char *)(io_2nd + BLKSIZE * (wblock-1)))[indwrite] = c;
  633.    indwrite = (indwrite+1) & (BLKSIZE-1);
  634. }
  635.  
  636. long writearch(h, length, name)
  637. int h; long length; char *name;
  638. {
  639.    register i; register j; register k;
  640.    register char *p;
  641.    register long b;
  642.  
  643.    if (!mblock) mblock = 1;
  644.  
  645.    b = 0;
  646.    while (length > 0) {
  647.       if (wblock >= mblock) {
  648.          if ((*pwrite)(io_2nd, BLKSIZE*mblock) < 0) wrerror();
  649.          if (v_flag > 1) {
  650.             (void)fputc('.', myout); (void)fflush(myout);
  651.          }
  652.          wblock = 0;
  653.       }
  654.       k = BLKSIZE * (mblock-wblock);
  655.       i = length < k ? (int)length : k;
  656.       p = io_2nd + BLKSIZE * wblock;
  657.       if ((j = read(h, p, i)) < 0) {
  658.          (void)fprintf(myout, "Tar: error reading \'%s\'\n", name);
  659.          done(ERREAD);
  660.       }
  661.       /* Calculate number of blocks affected */
  662.       k = (BLKSIZE-1 + j) / BLKSIZE;
  663.       wblock += k;
  664.       b      += k;
  665.       /* Fill the slack area */
  666.       for (p+=j, k=BLKSIZE*k-j; k>0; k--) *p++ = '\0';
  667.       if (j != i) return b;
  668.       length -= i;
  669.    }
  670.    if (wblock >= mblock) {
  671.       if ((*pwrite)(io_2nd, BLKSIZE*mblock) < 0) wrerror();
  672.       if (v_flag > 1) {
  673.          (void)fputc('.', myout); (void)fflush(myout);
  674.       }
  675.       wblock = 0;
  676.    }
  677.    if (v_flag > 1) (void)fputc('\n', myout);
  678.    indwrite = 0;
  679.    return b;
  680. }
  681.